#pragma once
#include <cerrno>
#include <cstring>
#include <iostream>
#include <functional>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "log.hpp"
#include "task.hpp"
#include "error.hpp"
#include "threadPool.hpp"

namespace ns_tcpserver
{
    // 完成简单的echoServer

    const uint16_t defaultport = 8888;
    const int backlog = 32; // 后面再解释这个参数 -- listen
    using func_t = std::function<std::string(std::string)>;
    class tcpServer; // 申明

    struct ThreadData
    {
        ThreadData(int fd, std::string name, tcpServer *cur) : _sock(fd), _clientname(name), _cur(cur)
        {
        }
        int _sock;
        std::string _clientname;
        tcpServer *_cur;
    };

    class tcpServer
    {
    public:
        tcpServer(func_t cb, uint16_t port = defaultport) : _server(cb), _port(port)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_cond, nullptr);
        }
        void initServer()
        {
            // std::cout << "tcpserver init..." << std::endl;
            logMessage(Debug, "tcpserver init...");
            // 1.创建监听套接字
            _listensock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listensock < 0)
            {
                // strerror:打印错误码对应的错误信息
                // std::cerr << "socket failed..." << strerror(errno) << std::endl;
                logMessage(Fatal, "socket failed...");

                exit(SOCKET_ERR);
            }
            logMessage(Info, "socket success...");

            // 2.绑定ip,port
            // 2.1.先填充server,注意转化为网络序列，注意ip地址不能固定
            struct sockaddr_in server;
            server.sin_family = AF_INET;
            server.sin_port = ntohs(_port);
            server.sin_addr.s_addr = ntohl(INADDR_ANY);
            if (bind(_listensock, (struct sockaddr *)&server, sizeof(server)) < 0)
            {
                // std::cerr << "bind failed..." << strerror(errno) << std::endl;
                logMessage(Fatal, "bind failed...");
                exit(BIND_ERR);
            }
            logMessage(Info, "bind success...");

            // 3.监听
            if (listen(_listensock, backlog) < 0)
            {
                // std::cerr << "listen failed..." << strerror(errno) << std::endl;
                logMessage(Fatal, "listen failed...");
                exit(LISTEN_ERR);
            }
            logMessage(Info, "listen success...");
            // 到这里初始化工作就完成了

        }
        void getResult(std::string str)
        {
            _response = str;
            // cout << "我已经写回结果..." << _response << std:: endl;
            pthread_cond_signal(&_cond);
        }
        // 处理服务也是死循环！ 你不可能只读取一次就结束了吧
        void service(int &sock, std::string &name)
        {
            // 让线程池，去处理短期任务。
            threadPool<Task> *tp = threadPool<Task>::getInstance();

            char buff[1024] = {0};
            while (true)
            {
                // 1.从对应的套接字中获取数据  -- 获取数据
                logMessage(Debug,"read before...");
                ssize_t n = read(sock, buff, sizeof(buff) - 1); //-1是当作字符串处理为/0预留位置
                logMessage(Debug,"read after...");

                if (n > 0)
                {
                    buff[n] = 0;
                    // std::cout << name << ">>>" << buff << std::endl;
                    logMessage(Info, "%s >>>%s", name.c_str(), buff);

                    // 2.业务处理
                    // 可以直接回调，使用上层传递的方法
                    // std::string res = _server(buff);

                    // 构建任务，交给线程池处理的任务队列
                    // 类内成员，需要绑定this
                    // Task t(buff,_server, getResult);
                    Task t(buff, _server, std::bind(&tcpServer::getResult, this, std::placeholders::_1));
                    tp->pushTask(t);

                    // 正确的解决线程之间的同步问题应该是要使用条件变量！！！
                    while (_response.empty())
                    {
                        // std::cout << "server.hpp:: wait" << std:: endl;
                        logMessage(Debug, "server.hpp:: wait");
                        pthread_cond_wait(&_cond, &_mutex);
                        // std::cout << "server.hpp:: waitup" << std:: endl;
                        logMessage(Debug, "server.hpp:: waitup");
                    }
                    // std:: cout << "我获取的结果 " << ++cnt << " "<< _response.c_str() << std:: endl;

                    // while(1)
                    // {
                    //     if(!_response.empty())
                    //         break;
                    // }
                    // sleep(1);

                    // std::cout << "write before..." << std:: endl;
                    write(sock, _response.c_str(), _response.size());
                    _response.clear(); // 你得清空，保证下一次，response是从read获取来的。

                    // read,send -- tcp会进行三次握手，建立连接，它们会交换彼此的信息 （ip,port等）
                    // 当连接建立成功后，这些信息会被保存下来
                    // 当进行数据传送时，read，send，就会根据这些保存下来的信息，传送到目的地
                }
                else if (n == 0)
                {
                    // 在tcp通信中，因为tcp是建立连接的，accept可以知道连接的建立，那什么时候知道连接关闭呢？
                    // 当对端关闭的时候，read会读到0个字符。所以当n==0时意味着对端关闭
                    // 我们要做的是关闭文件描述符
                    close(sock);
                    // std::cout << name << "close,me too" << std::endl;
                    logMessage(Warning, "%s close,me too", name.c_str());

                    break;
                }
                else
                {
                    // n < 0,意味着读取错误
                    close(sock);
                    // std::cerr << "read failed..." << strerror(errno) << std::endl;
                    logMessage(Error, "read failed... errno-%d  strerr-%s", errno, strerror(errno));

                    break;
                }
            }
        }

        static void *threadRoutine(void *args)
        {
            ThreadData *t = static_cast<ThreadData *>(args);
            t->_cur->service(t->_sock, t->_clientname);
        }

        void start()
        {
            // std::cout << "tcpserver start..." << std::endl;
            logMessage(Debug, "tcpserver start...");

            while (true)
            {
                // 4.获取连接
                struct sockaddr_in client;
                memset(&client, 0, sizeof(client));
                socklen_t len = sizeof(client);
                int sock = accept(_listensock, (struct sockaddr *)&client, &len); // 默认是阻塞式的
                if (sock < 0)
                {
                    // std::cerr << "accept failed..." << strerror(errno) << std::endl;
                    logMessage(Error, "accept failed... %s", strerror(errno));
                    continue;
                }
                // accept返回的套接字，才是真正给客户端提供服务的套接字！
                // 5.连接成功

                // 获取客户端信息
                std::string clientip = inet_ntoa(client.sin_addr);
                uint16_t clientport = ntohs(client.sin_port);
                std::string clientname = clientip + "-";
                clientname += std::to_string(clientport);
                // debug测试
                // std::cout << "accept success... " << sock << " from " << _listensock << " " << clientname << std::endl;
                logMessage(Info, "accept success... %d  from %d %s", sock, _listensock, clientname.c_str());

                // service(sock, clientname); // 提供服务，进行收取数据，数据处理，发送数据

                // 不能去改Thread.hpp这个组件，因为你在threadPool.hpp中固定了它的用法，你只能使用原生线程库
                pthread_t t;
                // data是必须是new出来的，不然会导致多个线程使用的是同一个data
                ThreadData *data = new ThreadData(sock, clientname, this);
                pthread_create(&t, nullptr, threadRoutine, data);
            }
        }

        ~tcpServer()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_cond);
        }

    private:
        uint16_t _port;
        int _listensock;
        func_t _server;
        // 因为时序的问题，不能保证先执行业务处理，再write，我们使用回调，将结果写会来
        std::string _response; // 通过回调，从而获得处理的结果
        // 上面的回调并不能解决问题，我们必须通过线程同步去解决它们步调不一致的问题
        pthread_cond_t _cond;
        pthread_mutex_t _mutex;
    };
}